﻿using System;
using System.Collections.Generic;
using UnityEngine;

public class Common
{
    public static IShipAPI Ship;
    public static ITextTerminal TextTerminal;
    public static IMemory Memory;
    public static GlobalData Global;
    public static IStats Stats;

    public static void SetFields(ShipProgram program)
    {
        Ship = program.Ship;
        TextTerminal = program.TextTerminal;
        Memory = program.Memory;
        Global = program.Global;
        Stats = program.Stats;
    }

    public static void Init()
    {
        AddCommand("shoot", "Shoot laser gun", args => Ship.ShootGun());
        AddCommand("moveto", " [x] [y] [z] : Fly to set point", args =>
        {
            Vector3 point = new Vector3(int.Parse(args[1]), int.Parse(args[2]), int.Parse(args[3]));
            Ship.AttitudeAutopilotEnabled = true;
            Ship.ThrustAutopilotEnabled = true;
            Ship.TargetPoint = point;
        }, 3);
        AddCommand("nearscan", "Scan nearest surroundings", args => NearScanCommand());
        AddCommand("stop", "Stop the ship", args =>
        {
            Ship.AttitudeAutopilotEnabled = false;
            Ship.ThrustAutopilotEnabled = false;
            Ship.TargetPoint = Ship.ShipPosition;
        });
        
        AddCommand("lookat"," [x] [y] [z] : Look at set point", args=> LookAtCommand(args), 3);
        AddCommand("raycast", "forward raycast", args=>Raycast());
        AddCommand("thrust", "control thrust", args=> Thrust());
        AddCommand("forward", "move ship forward by distance", args => Forward(args), 1);
    }

    private static void AddCommand(String name, String description, Action<string[]> action)
    {
        var command = new Command();
        command.Description = description;
        command.Name = name;
        command.action = action;
        _commands.Add(name, command);
    }
    
    private static void AddCommand(String name, String description, Action<string[]> action, int paramCount)
    {
        var command = new Command();
        command.Description = description;
        command.Name = name;
        command.action = action;
        command.ParamCount = paramCount;
        _commands.Add(name, command);
    }

    private static void Raycast()
    {
        EntitySummary summary = new EntitySummary();
        //Ship.Radar.Raycast(Ship.TargetPoint, 15, out summary);
            /*TextTerminal.AddMessage(string.Format("Type: {0} ID: {1}, Distance: {2}, Position: {3}\nSummary: {4} ",
            summary.Type, 
            summary.ID, 
            Mathf.RoundToInt(Vector3.Distance(Ship.ShipPosition, summary.Position)), 
            summary.Position.ToString("F0"), (object) summary.Summary));*/
    }

    private static void Thrust()
    {
        var forward = Ship.DirectionLocalToGlobal(new Vector3(1, 0,0));
        Ship.TargetPoint = Ship.ShipPosition + forward;
        Ship.AttitudeAutopilotEnabled = true;
        Ship.ThrustAutopilotEnabled = true;
        Ship.CollectAllCargo();
    }
    
    private static void Forward(string[] args)
    {
        var angles = Ship.EulerAngles;
        var forward = Quaternion.Euler(angles) *  Vector3.forward * int.Parse(args[1]);
        Ship.TargetPoint = Ship.ShipPosition + forward;
        Ship.AttitudeAutopilotEnabled = true;
        Ship.ThrustAutopilotEnabled = true;
    }

    public static void ExternalCall(ITextTerminal TextTerminal)
    {
        TextTerminal.Clear();
        TextTerminal.AddMessage("Hello world!\n Print 'help' to get list of available commands");
    }

    protected static void NearScanCommand()
    {
        List<EntitySummary> entitySummaryList = new List<EntitySummary>();
        Ship.Radar.NearScan(100f, entitySummaryList);
        TextTerminal.AddMessage(string.Format("Near Scan complete. Found {0} entities", (object) entitySummaryList.Count));
        PrintScanResult(entitySummaryList);
    }

    protected static void FarScanCommand()
    {
        List<EntitySummary> entitySummaryList = new List<EntitySummary>();
        Ship.Radar.ScanForLargeObjects(500f, entitySummaryList);
        TextTerminal.AddMessage(string.Format("Far Scan complete. Found {0} entities", (object) entitySummaryList.Count));
        PrintScanResult(entitySummaryList);
    }

    private static void PrintScanResult(List<EntitySummary> list)
    {
        Vector3 shipPosition = Ship.ShipPosition;
        int num = 1;
        foreach (EntitySummary entitySummary in list)
        {
            TextTerminal.AddMessage(string.Format("{0}: Type: {1} ID: {2}, Distance: {3}, Position: {4}\nSummary: {5} ", 
                (object) num, 
                (object) entitySummary.Type, 
                (object) entitySummary.ID, 
                (object) Mathf.RoundToInt(Vector3.Distance(shipPosition, entitySummary.Position)), 
                (object) entitySummary.Position.ToString("F0"), (object) entitySummary.Summary));
            ++num;
        }
    }
    
    public static void ParseInput(string input)
    {
        string[] args = input.Trim().ToLower().Split(' ');
        if (args.Length == 0)
        {
            TextTerminal.AddMessage("Empty command");
        }

        _commands[args[0]].Execute(args);
    }
    
    protected static void LookAtCommand(string[] args)
    {
        int result1;
        int.TryParse(args[1], out result1);
        int result2;
        int.TryParse(args[2], out result2);
        int result3;
        int.TryParse(args[3], out result3);
        Vector3 vector3_1 = new Vector3((float) result1, (float) result2, (float) result3);
        Vector3 shipPosition = Ship.ShipPosition;
        Vector3 vector3_2 = shipPosition;
        Vector3 vector3_3 = Vector3.ClampMagnitude(vector3_1 - vector3_2, 1f);
        Ship.TargetPoint = shipPosition + vector3_3;
    }

    private static Dictionary<String, Command> _commands = new Dictionary<string, Command>();

    class Command
    {
        public Action<String[]> action;
        public String Name;
        public string Description;
        public int ParamCount = 0;

        public void Execute(string[] args)
        {
            Validate(args);
            action.Invoke(args);
        }

        private void Validate(string[] args)
        {
            if (args.Length != 1 + ParamCount)
                {
                    TextTerminal.AddMessage("Wrong argument size. Expected: 4, Actual: " + args.Length);
                }
        }
    }
}